package com.umgsai.wx.backend.test;

import cn.hutool.core.util.NumberUtil;
import com.google.common.collect.Lists;
import com.umgsai.wx.backend.BaseTest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
public class EncodeUtil extends BaseTest {

	@Resource(name = "commonExecutor")
	private ThreadPoolExecutor commonExecutor;

	public static String DICTIONARY = "0123456789ABCEFGHJKLMNPQRSTUVWXYZ";
	private static final BigInteger SCALE62 = new BigInteger(String.valueOf(DICTIONARY.length()), 10);
	private static final BigInteger SCALE62_MAX_INDEX = new BigInteger(String.valueOf(DICTIONARY.length() - 1), 10);

	/**
	 * 将数字转为62进制，采用短除法计算方式
	 *
	 * @param numString 数字字符串
	 * @return 62进制字符串
	 */
	public static String encode(String numString) {
		if (StringUtils.isEmpty(numString) || !NumberUtil.isNumber(numString)) {
			throw new NumberFormatException("Zero length BigInteger");
		}
		return encode(new BigInteger(numString));
	}

	/**
	 * 将数字转为62进制，采用短除法计算方式
	 *
	 * @param num Long 型数字
	 * @return 62进制字符串
	 */
	public static String encode(BigInteger num) {
		StringBuilder sb = new StringBuilder();
		BigInteger remainder = null;
		while (num.compareTo(SCALE62_MAX_INDEX) > 0) {
			// 对 scale 进行求余，然后将余数追加至 sb 中，由于是从末位开始追加的，因此最后需要反转（reverse）字符串
			remainder = num.mod(SCALE62);
			sb.append(DICTIONARY.charAt(remainder.intValue()));
			num = num.divide(SCALE62);
		}
		sb.append(DICTIONARY.charAt(num.intValue()));
		return sb.reverse().toString();
	}

	/**
	 * 62进制字符串转为数字
	 *
	 * @param str 编码后的62进制字符串
	 * @return 解码后的 10 进制字符串
	 */
	public static String decode(String str) {
		if (StringUtils.isEmpty(str)) {
			throw new NumberFormatException("Zero length BigInteger");
		}
		// 将 0 开头的字符串进行替换
		str = str.replace("^0*", "");
		BigInteger num = new BigInteger("0");
		int index;
		for (int i = 0; i < str.length(); i++) {
			// 查找字符的索引位置
			index = DICTIONARY.indexOf(str.charAt(i));

			// 索引位置代表字符的数值
			BigInteger bigIndex = new BigInteger(String.valueOf(index), 10);
			BigInteger temp = SCALE62.pow(str.length() - i - 1);
			num = num.add(bigIndex.multiply(temp));
		}
		return num.toString();
	}

	@Test
	public void test() {
//		String couponNumber = "123456789012";
//		log.println("couponNumber:" + couponNumber);
//		int crc12 = CRC12Test.calculateCRC12(couponNumber);
//		log.println("crc12:" + crc12);
//
//		String encode = encode(couponNumber + crc12);
//		log.println("encode:" + encode);
//		String decode = decode(encode);
//		log.println("decode:" + decode);

		List<CompletableFuture<?>> completableFutureList = Lists.newArrayList();
		for (long i = 0L; i < 2174299063698048L; i++) {
			final long finalI = i;
			completableFutureList.add(CompletableFuture.runAsync(() -> {
				try {
					encodeAndCheck(finalI);
				} catch (Exception e) {
					log.error("exception", e);
				}
			}, commonExecutor));

			if (completableFutureList.size() >= 64) {
				CompletableFuture<?>[] futureArray = new CompletableFuture[completableFutureList.size()];
				CompletableFuture.allOf(completableFutureList.toArray(futureArray)).join();
				completableFutureList.clear();
				log.info("i:" + i + " done.");
			}
		}
	}

	private void encodeAndCheck(Long i) {
//		log.info("couponNo:" + i);
		// 原始券号16位，前面补0
		String str = String.format("%016d", i);
		// 计算CRC，只取最后4位，位数不够前面补0
		String crcString = calcCrc(i);
		// 一共20位进行编码
		String encode = encode(str + crcString);

		encode = "0000000000000000" + encode;
		// 编码后保留14位
		encode = StringUtils.right(encode, 14);

		StringBuilder stringBuilder = new StringBuilder(encode);
		// 逆序
		stringBuilder.reverse();
		encode = stringBuilder.toString();
//		log.info("encode:" + encode);

		// 逆序还原
		stringBuilder.reverse();
		encode = stringBuilder.toString();
		// 解码，得到20位
		String decode = decode(encode);
		// 左边补0
		decode = "0000000000000000" + decode;
		// 取最后20位
		decode = StringUtils.right(decode, 20);
		// 最后4位是crc
		String crc = StringUtils.right(decode, 4);
		// 前面14位是原始数字，前面可能有0
		decode = StringUtils.left(decode, 16);
		// 转成数字得到原始券号
		Long couponNumber = Long.valueOf(decode);
//		log.info("decode:" + decode);
		// 用解码出的券号重新计算crc
		String calcCrcString = calcCrc(couponNumber);
		// 对比重新计算的CRC与解码出的CRC是否一致
		boolean equals = StringUtils.equals(calcCrcString, crc);
//		log.info("check:" + equals);
		if (!equals) {
			log.error("check:" + equals);
			log.error("couponNo:" + i);
		}
	}


	private static String calcCrc(Long num) {
		String str = String.format("%016d", num);
		int crc12 = CRC12Test.calculateCRC12(str);
		String crcString = String.valueOf(crc12);
		crcString = "0000" + crcString;
		return StringUtils.right(crcString, 4);
	}
}
